package com.nebula.core.dsl.operation;

import com.nebula.core.constant.Constant;
import com.nebula.core.entity.vo.Edge;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.nebula.core.constant.Constant.EDGE_VID_FORMAT;
import static com.nebula.core.constant.Constant.QUOTED_LITERAL_FORMAT;


/**
 * 边操作类
 *
 * @author zhangtao
 */
@Slf4j
public class EdgeOperation {
    /**
     * 插入边
     *
     * @param edgeType
     * @param edgeList
     * @return
     */
    protected static String insertEdge(String edgeType, List<Edge> edgeList) {
        if (StringUtils.isBlank(edgeType)) {
            throw new IllegalArgumentException("edgeType is not empty");
        }

        if (CollectionUtils.isEmpty(edgeList)) {
            throw new IllegalArgumentException("edgeList is not empty");
        }

        List<String> columns = new ArrayList<>();
        if (!CollectionUtils.isEmpty(edgeList.get(0).getProperties())) {
            for (Map.Entry<String, Object> entry : edgeList.get(0).getProperties().entrySet()) {
                if (Constant.SRC_ID.equals(entry.getKey()) || Constant.DST_ID.equals(entry.getKey())) {
                    continue;
                }
                columns.add(entry.getKey());
            }
        }

        StringBuilder ngql = new StringBuilder("INSERT EDGE ");
        ngql.append(edgeType);
        ngql.append(columns.stream().collect(Collectors.joining(",", "(", ")")));

        ngql.append(" VALUES ");
        StringJoiner edgeJoin = new StringJoiner(",");
        for (Edge edge : edgeList) {
            StringBuilder edgeBuilder = new StringBuilder();
            edgeBuilder.append(String.format(EDGE_VID_FORMAT, edge.getSrcId(), edge.getDstId()));

            edgeBuilder.append("@");
            edgeBuilder.append(edge.getRank());
            edgeBuilder.append(":(");

            StringJoiner valuesJoin = new StringJoiner(",", edgeBuilder.toString(), ")");

            if (!CollectionUtils.isEmpty(edge.getProperties())) {
                for (Map.Entry<String, Object> entry : edge.getProperties().entrySet()) {
                    if (Constant.SRC_ID.equals(entry.getKey()) || Constant.DST_ID.equals(entry.getKey())) {
                        continue;
                    }
                    Object value = entry.getValue();
                    if (value instanceof Number) {
                        valuesJoin.add(value.toString());
                    } else if (value instanceof Date) {
                        Date d = (Date) value;
                        valuesJoin.add(String.format(Constant.TIMESTAMP_FORMAT, d.getTime() / 1000));
                    } else {
                        valuesJoin.add(String.format(Constant.QUOTED_LITERAL_FORMAT, value));
                    }
                }
            }
            edgeJoin.add(valuesJoin.toString());
        }


        ngql.append(edgeJoin);
        return ngql.toString();
    }

    /**
     * 删除边
     *
     * @param edgeType
     * @param edges
     * @return
     */
    protected static String deleteEdge(String edgeType, Edge... edges) {
        if (StringUtils.isBlank(edgeType)) {
            throw new IllegalArgumentException("edgeType is not empty");
        }

        if (edges.length == 0) {
            throw new IllegalArgumentException("edges is not empty");
        }

        StringBuilder ngql = new StringBuilder("DELETE EDGE ");
        ngql.append(edgeType);
        ngql.append(Arrays.stream(edges)
                .map(e -> String.format(EDGE_VID_FORMAT, e.getSrcId(), e.getDstId()))
                .collect(Collectors.joining(",", " ", " ")));
        return ngql.toString();
    }

    protected static String updateEdge(String edgeType, Edge edge) {
        if (StringUtils.isBlank(edgeType)) {
            throw new IllegalArgumentException("edgeType is not empty");
        }


        StringBuilder ngql = new StringBuilder("UPDATE EDGE ON ");
        ngql.append(edgeType);

        ngql.append(String.format(EDGE_VID_FORMAT, edge.getSrcId(), edge.getDstId()));

        StringJoiner valuesJoin = new StringJoiner(",", " SET ", "");

        if (!CollectionUtils.isEmpty(edge.getProperties())) {
            for (Map.Entry<String, Object> entry : edge.getProperties().entrySet()) {
                Object value = entry.getValue();
                if (value instanceof Number) {
                    valuesJoin.add(String.format("%s = %s", entry.getKey(), value));
                } else if (value instanceof Date) {
                    Date d = (Date) value;
                    valuesJoin.add(String.format("%s = timestamp(%s)", entry.getKey(), d.getTime() / 1000));
                } else {
                    valuesJoin.add(String.format("%s = '%s'", entry.getKey(), value));
                }
            }
        }

        ngql.append(valuesJoin);
        return ngql.toString();
    }

}
